Asenkron eylemlerle tetiklenen modern durum yönetimi için React'in useActionState hook'unu keşfedin. Uygulamanızın verimliliğini ve kullanıcı deneyimini geliştirin.
React useActionState Uygulaması: Eylem Tabanlı Durum Yönetimi
React'in son sürümlerinde tanıtılan useActionState hook'u, asenkron eylemlerden kaynaklanan durum güncellemelerini yönetmek için rafine bir yaklaşım sunar. Bu güçlü araç, özellikle React Sunucu Bileşenleri (RSC) ve sunucu eylemleriyle çalışırken mutasyonları ele alma, arayüzü güncelleme ve hata durumlarını yönetme sürecini kolaylaştırır. Bu kılavuz, useActionState'in inceliklerini keşfedecek, pratik örnekler ve uygulama için en iyi pratikleri sunacaktır.
Eylem Tabanlı Durum Yönetiminin Gerekliliğini Anlamak
Geleneksel React durum yönetimi genellikle bileşenler içinde yüklenme ve hata durumlarının ayrı ayrı yönetilmesini içerir. Bir eylem (örneğin, bir form gönderme, veri çekme) bir durum güncellemesini tetiklediğinde, geliştiriciler genellikle bu durumları birden çok useState çağrısı ve potansiyel olarak karmaşık koşullu mantıkla yönetir. useActionState, daha temiz ve daha entegre bir çözüm sunar.
Basit bir form gönderme senaryosunu düşünün. useActionState olmadan, şunlara sahip olabilirsiniz:
- Form verileri için bir durum değişkeni.
- Formun gönderilip gönderilmediğini izlemek için bir durum değişkeni (yüklenme durumu).
- Olası hata mesajlarını tutmak için bir durum değişkeni.
Bu yaklaşım, çok sözlü koda ve potansiyel tutarsızlıklara yol açabilir. useActionState, bu endişeleri tek bir hook'ta birleştirerek mantığı basitleştirir ve kod okunabilirliğini artırır.
useActionState'e Giriş
useActionState hook'u iki argüman kabul eder:
- Durum güncellemesini gerçekleştiren asenkron bir fonksiyon ("eylem"). Bu bir sunucu eylemi veya herhangi bir asenkron fonksiyon olabilir.
- Bir başlangıç durumu değeri.
İki eleman içeren bir dizi döndürür:
- Mevcut durum değeri.
- Eylemi göndermek için bir fonksiyon. Bu fonksiyon, eylemle ilişkili yüklenme ve hata durumlarını otomatik olarak yönetir.
İşte temel bir örnek:
import { useActionState } from 'react';
async function updateServer(prevState, formData) {
// Asenkron bir sunucu güncellemesini simüle et.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
return 'Sunucu güncellenemedi.';
}
return `İsim şuna güncellendi: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Başlangıç Durumu');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
Bu örnekte:
updateServer, bir sunucuyu güncellemeyi simüle eden asenkron eylemdir. Önceki durumu ve form verilerini alır.useActionState, durumu 'Başlangıç Durumu' ile başlatır ve mevcut durumu iledispatchfonksiyonunu döndürür.handleSubmitfonksiyonu, form verileriyledispatch'i çağırır.useActionState, eylem yürütme sırasında yüklenme ve hata durumlarını otomatik olarak yönetir.
Yüklenme ve Hata Durumlarını Yönetme
useActionState'in temel faydalarından biri, yüklenme ve hata durumlarını dahili olarak yönetmesidir. dispatch fonksiyonu, eylemin sonucuyla çözümlenen bir promise döndürür. Eğer eylem bir hata fırlatırsa, promise hata ile reddedilir. Bunu arayüzü uygun şekilde güncellemek için kullanabilirsiniz.
Önceki örneği bir yüklenme mesajı ve bir hata mesajı gösterecek şekilde değiştirelim:
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// Asenkron bir sunucu güncellemesini simüle et.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Sunucu güncellenemedi.');
}
return `İsim şuna güncellendi: ${data.name}`;
}
function MyComponent() {
const [state, dispatch] = useActionState(updateServer, 'Başlangıç Durumu');
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
setIsSubmitting(true);
setErrorMessage(null);
try {
const result = await dispatch(formData);
console.log(result);
} catch (error) {
console.error("Gönderim sırasında hata:", error);
setErrorMessage(error.message);
} finally {
setIsSubmitting(false);
}
}
return (
);
}
Temel değişiklikler:
- Yüklenme ve hata durumlarını izlemek için
isSubmittingveerrorMessagedurum değişkenleri ekledik. handleSubmitiçinde,dispatch'i çağırmadan önceisSubmitting'itrueolarak ayarladık veerrorMessage'i güncellemek için olası hataları yakaladık.- Gönderim sırasında gönder düğmesini devre dışı bıraktık ve yüklenme ile hata mesajlarını koşullu olarak gösterdik.
React Sunucu Bileşenlerinde (RSC) Sunucu Eylemleri ile useActionState
useActionState, React Sunucu Bileşenleri (RSC) ve sunucu eylemleriyle kullanıldığında gerçekten parlar. Sunucu eylemleri, sunucuda çalışan ve veri kaynaklarını doğrudan değiştirebilen fonksiyonlardır. API uç noktaları yazmadan sunucu tarafı işlemler yapmanıza olanak tanırlar.
Not: Bu örnek, Sunucu Bileşenleri ve Sunucu Eylemleri için yapılandırılmış bir React ortamı gerektirir.
// app/actions.js (Sunucu Eylemi)
'use server';
import { cookies } from 'next/headers'; //Örnek, Next.js için
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return 'Lütfen bir isim girin.';
}
try {
// Veritabanı güncellemesini simüle et.
await new Promise(resolve => setTimeout(resolve, 1000));
cookies().set('userName', name);
return `İsim şuna güncellendi: ${name}`; //Başarılı!
} catch (error) {
console.error("Veritabanı güncellemesi başarısız:", error);
return 'İsim güncellenemedi.'; // Önemli: Bir Hata fırlatmak yerine bir mesaj döndürün
}
}
// app/page.jsx (React Sunucu Bileşeni)
'use client';
import { useActionState } from 'react';
import { updateName } from './actions';
function MyComponent() {
const [state, dispatch] = useActionState(updateName, 'Başlangıç Durumu');
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const result = await dispatch(formData);
console.log(result);
}
return (
);
}
export default MyComponent;
Bu örnekte:
updateName,app/actions.jsiçinde tanımlanmış bir sunucu eylemidir. Önceki durumu ve form verilerini alır, veritabanını günceller (simüle edilmiştir) ve bir başarı veya hata mesajı döndürür. Önemli olan, eylemin bir hata fırlatmak yerine bir mesaj döndürmesidir. Sunucu Eylemleri bilgilendirici mesajlar döndürmeyi tercih eder.- Bileşen,
useActionStatehook'unu kullanabilmek için bir istemci bileşeni olarak işaretlenmiştir ('use client'). handleSubmitfonksiyonu, form verileriyledispatch'i çağırır.useActionState, sunucu eyleminin sonucuna göre durum güncellemesini otomatik olarak yönetir.
Sunucu Eylemleri için Önemli Hususlar
- Sunucu Eylemlerinde Hata Yönetimi: Hata fırlatmak yerine, Sunucu Eyleminizden anlamlı bir hata mesajı döndürün.
useActionStatebu mesajı yeni durum olarak ele alacaktır. Bu, istemcide zarif bir hata yönetimi sağlar. - İyimser Güncellemeler: Sunucu eylemleri, algılanan performansı artırmak için iyimser güncellemelerle kullanılabilir. Arayüzü hemen güncelleyebilir ve eylem başarısız olursa geri alabilirsiniz.
- Yeniden Doğrulama: Başarılı bir mutasyondan sonra, arayüzün en son durumu yansıttığından emin olmak için önbelleğe alınmış verileri yeniden doğrulamayı düşünün.
Gelişmiş useActionState Teknikleri
1. Karmaşık Durum Güncellemeleri için Reducer Kullanımı
Daha karmaşık durum mantığı için, useActionState'i bir reducer fonksiyonuyla birleştirebilirsiniz. Bu, durum güncellemelerini öngörülebilir ve sürdürülebilir bir şekilde yönetmenizi sağlar.
import { useActionState } from 'react';
import { useReducer } from 'react';
const initialState = {
count: 0,
message: 'Başlangıç Durumu',
};
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
case 'SET_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
}
async function updateState(state, action) {
// Asenkron işlemi simüle et.
await new Promise(resolve => setTimeout(resolve, 500));
switch (action.type) {
case 'INCREMENT':
return reducer(state, action);
case 'DECREMENT':
return reducer(state, action);
case 'SET_MESSAGE':
return reducer(state, action);
default:
return state;
}
}
function MyComponent() {
const [state, dispatch] = useActionState(updateState, initialState);
return (
Sayı: {state.count}
Mesaj: {state.message}
);
}
2. useActionState ile İyimser Güncellemeler
İyimser güncellemeler, eylem başarılıymış gibi arayüzü hemen güncelleyerek ve eylem başarısız olursa güncellemeyi geri alarak kullanıcı deneyimini iyileştirir. Bu, uygulamanızın daha duyarlı hissettirmesini sağlayabilir.
import { useActionState } from 'react';
import { useState } from 'react';
async function updateServer(prevState, formData) {
// Asenkron bir sunucu güncellemesini simüle et.
await new Promise(resolve => setTimeout(resolve, 1000));
const data = Object.fromEntries(formData);
if (data.name === "error") {
throw new Error('Sunucu güncellenemedi.');
}
return `İsim şuna güncellendi: ${data.name}`;
}
function MyComponent() {
const [name, setName] = useState('Başlangıç İsmi');
const [state, dispatch] = useActionState(async (prevName, newName) => {
try {
const result = await updateServer(prevName, {
name: newName,
});
return newName; // Başarı durumunda güncelle
} catch (error) {
// Hata durumunda geri al
console.error("Güncelleme başarısız:", error);
setName(prevName);
return prevName;
}
}, name);
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const newName = formData.get('name');
setName(newName); // Arayüzü iyimser bir şekilde güncelle
await dispatch(newName);
}
return (
);
}
3. Eylemleri Debounce Etme
Bazı senaryolarda, eylemlerin çok sık gönderilmesini önlemek için debounce etmek isteyebilirsiniz. Bu, yalnızca kullanıcı belirli bir süre yazmayı bıraktıktan sonra bir eylem tetiklemek istediğiniz arama girişleri gibi senaryolar için yararlı olabilir.
import { useActionState } from 'react';
import { useState, useEffect } from 'react';
async function searchItems(prevState, query) {
// Asenkron aramayı simüle et.
await new Promise(resolve => setTimeout(resolve, 500));
return `Şunun için arama sonuçları: ${query}`;
}
function MyComponent() {
const [query, setQuery] = useState('');
const [state, dispatch] = useActionState(searchItems, 'Başlangıç Durumu');
useEffect(() => {
const timeoutId = setTimeout(() => {
if (query) {
dispatch(query);
}
}, 300); // 300ms için debounce et
return () => clearTimeout(timeoutId);
}, [query, dispatch]);
return (
setQuery(e.target.value)}
/>
Durum: {state}
);
}
useActionState için En İyi Pratikler
- Eylemleri Saf Tutun: Eylemlerinizin saf fonksiyonlar (veya mümkün olduğunca saf) olduğundan emin olun. Durumu güncellemek dışında yan etkileri olmamalıdır.
- Hataları Zarifçe Yönetin: Eylemlerinizdeki hataları her zaman yönetin ve kullanıcıya bilgilendirici hata mesajları sağlayın. Sunucu Eylemleri ile yukarıda belirtildiği gibi, bir hata fırlatmak yerine sunucu eyleminden bir hata mesajı dizesi döndürmeyi tercih edin.
- Performansı Optimize Edin: Özellikle büyük veri setleriyle uğraşırken eylemlerinizin performans etkilerini göz önünde bulundurun. Gereksiz yeniden render'ları önlemek için memoization tekniklerini kullanmayı düşünün.
- Erişilebilirliği Dikkate Alın: Uygulamanızın engelliler de dahil olmak üzere tüm kullanıcılar için erişilebilir olduğundan emin olun. Uygun ARIA nitelikleri ve klavye navigasyonu sağlayın.
- Kapsamlı Test: Eylemlerinizin ve durum güncellemelerinizin doğru çalıştığından emin olmak için birim testleri ve entegrasyon testleri yazın.
- Uluslararasılaştırma (i18n): Küresel uygulamalar için, birden çok dili ve kültürü desteklemek üzere i18n uygulayın.
- Yerelleştirme (l10n): Yerelleştirilmiş içerik, tarih formatları ve para birimi sembolleri sağlayarak uygulamanızı belirli yerel ayarlara göre uyarlayın.
useActionState vs. Diğer Durum Yönetimi Çözümleri
useActionState, eylem tabanlı durum güncellemelerini yönetmek için uygun bir yol sağlasa da, tüm durum yönetimi çözümlerinin yerine geçmez. Birden çok bileşen arasında paylaşılması gereken küresel duruma sahip karmaşık uygulamalar için Redux, Zustand veya Jotai gibi kütüphaneler daha uygun olabilir.
useActionState ne zaman kullanılır:
- Basit ila orta karmaşıklıktaki durum güncellemeleri.
- Asenkron eylemlerle sıkı sıkıya bağlı durum güncellemeleri.
- React Sunucu Bileşenleri ve Sunucu Eylemleri ile entegrasyon.
Ne zaman diğer çözümler düşünülmeli:
- Karmaşık küresel durum yönetimi.
- Çok sayıda bileşen arasında paylaşılması gereken durum.
- Zaman yolculuğuyla hata ayıklama veya middleware gibi gelişmiş özellikler.
Sonuç
React'in useActionState hook'u, asenkron eylemler tarafından tetiklenen durum güncellemelerini yönetmek için güçlü ve zarif bir yol sunar. Yüklenme ve hata durumlarını birleştirerek, özellikle React Sunucu Bileşenleri ve sunucu eylemleriyle çalışırken kodu basitleştirir ve okunabilirliği artırır. Güçlü ve zayıf yönlerini anlamak, uygulamanız için doğru durum yönetimi yaklaşımını seçmenize olanak tanır, bu da daha sürdürülebilir ve verimli bir kodla sonuçlanır.
Bu kılavuzda özetlenen en iyi pratikleri izleyerek, uygulamanızın kullanıcı deneyimini ve geliştirme iş akışını geliştirmek için useActionState'i etkili bir şekilde kullanabilirsiniz. Uygulamanızın karmaşıklığını göz önünde bulundurmayı ve ihtiyaçlarınıza en uygun durum yönetimi çözümünü seçmeyi unutmayın. Basit form gönderimlerinden karmaşık veri mutasyonlarına kadar, useActionState React geliştirme cephaneliğinizde değerli bir araç olabilir.